Skip to content

[REFACTOR] 폴더 구조 정리 및 쿼리/뮤테이션 훅 네이밍 컨벤션 적용 - #219

Merged
kimminna merged 2 commits into
developfrom
refactor/web/217-query-naming-convention
Jul 15, 2026
Merged

[REFACTOR] 폴더 구조 정리 및 쿼리/뮤테이션 훅 네이밍 컨벤션 적용#219
kimminna merged 2 commits into
developfrom
refactor/web/217-query-naming-convention

Conversation

@kimminna

Copy link
Copy Markdown
Member

ISSUE 🔗

close #217



What is this PR? 🔍

api/common/, types/, hooks/ 루트에 흩어져 있던 스키마·타입·훅 파일을 도메인별 폴더로 재정리하고, 그 과정에서 깨진 import 경로를 전부 수정했습니다. 동시에 TanStack Query 훅의 파일명·함수명을 -query/-mutation, ~Query/~Mutation 컨벤션으로 통일하고, 이 컨벤션을 문서에 명문화했습니다.

배경

  • 기존 구조: 스키마(api/common/)·타입(types/)·타이머 훅(hooks/)·오버레이 프로바이더가 도메인 구분 없이 루트에 흩어져 있었고, 쿼리/뮤테이션 훅은 파일명·함수명 규칙이 통일되어 있지 않았습니다 (useTerms, useWithdrawAction, useLogoutAction 등 제각각).
  • 발생 문제: 폴더를 도메인별로 이동하는 과정에서 다수의 import 경로가 갱신되지 않아 타입체크가 깨져 있었고, 쿼리/뮤테이션 훅 이름만으로는 조회인지 변경인지 구분하기 어려웠습니다.
  • 해결 방향: 스키마/타입/훅을 도메인 폴더로 정리하고 모든 참조를 갱신했습니다. 쿼리 훅은 ~Query/-query.ts, 뮤테이션 훅은 ~Mutation/-mutation.ts로 통일하고, 이 규칙을 docs/conventions/naming.mdtimo-api-integration 스킬에 반영해 앞으로도 동일하게 적용되도록 했습니다.

폴더 구조 재정리

  • 변경 요약: api/common/의 zod 스키마, types/의 도메인 타입, hooks/의 타이머 훅, providers/OverlayProvider를 각각 schemas/{domain}/, hooks/timer/, providers/overlay/로 이동했습니다.
  • 이유: 도메인 구분 없이 루트에 평평하게 쌓여 있어 파일이 늘어날수록 어떤 파일이 어떤 기능에 속하는지 파악하기 어려웠습니다.
  • 구현 방식: 파일 내용은 그대로 두고 위치만 도메인 단위 폴더로 이동했습니다. 이동으로 인해 깨진 import 경로는 tsc --noEmit으로 전수 검증하며 하나씩 수정했습니다 — TimeboxPanel.tsx@/queries/use-time-boxes, TodayTodoCardContainer.tsxtoday/_components/TodayTodoCard 등 도메인 라우트 그룹((with-time-sidebar)) 이동 과정에서 누락된 경로도 함께 잡았습니다.

쿼리/뮤테이션 훅 네이밍

  • 변경 요약: useMyProfile, useTerms, useTags, useTimeBoxes, useCreateTag, useDeleteTag, useToday, useFocusTodo, useHomeView, useWithdrawAction, useLogoutAction 11개 훅의 파일명·함수명을 ~Query/~Mutation 접미사로 통일했습니다 (예: useWithdrawActionuseWithdrawMutation, use-terms.tsuse-terms-query.ts).
  • 이유: 함수명만 보고 조회 훅인지 변경 훅인지 구분할 수 없어 호출부에서 부수효과 여부를 파악하려면 매번 구현을 열어봐야 했습니다.
  • 구현 방식: useSuspenseQuery/useQuery를 감싸는 훅은 ~Query, useMutation을 감싸는 훅은 ~Mutation으로 이름을 바꾸고 파일명도 동일한 접미사(-query.ts/-mutation.ts)로 맞췄습니다. 이 훅들을 사용하는 10개 소비 파일의 import 경로와 호출부도 함께 갱신했습니다. hooks/timer/use-active-timer.ts처럼 react-query 조회 결과에 타이머 tick 같은 추가 상태·로직을 합성한 훅은 순수 조회/변경 래퍼가 아니므로 이번 컨벤션 대상에서 제외했습니다.
  • 경계 · 제약: 도메인별로 이미 여러 조회 훅을 한 파일에 묶어 두었던 statistics/_queries/statistics-queries.ts는 파일 분리 대신 함수명이 이미 ~Query로 끝나는 점을 살려 파일명만 use-statistics-query.ts로 맞추고 훅 3개는 한 파일에 유지했습니다 — 도메인당 조회 훅이 늘어날 때마다 파일을 잘게 쪼개는 대신, 이미 응집도 높게 묶여 있던 단위는 그대로 두는 쪽을 선택했습니다.

컨벤션 문서화

  • 변경 요약: docs/conventions/naming.md에 TanStack Query 훅의 파일명·함수명 규칙 섹션을 추가하고, timo-api-integration 스킬의 _queries/ 훅 작성 가이드 예시 코드와 자가 검토 체크리스트를 새 컨벤션에 맞게 갱신했습니다.
  • 이유: 컨벤션이 코드에만 반영되고 문서화되지 않으면 다음 API 연동 작업에서 다시 규칙이 흐트러질 수 있습니다.
  • 구현 방식: 조회/변경 훅 각각의 파일·함수 네이밍 예시와 "파일당 훅 1개가 기본" 원칙, 그리고 합성 훅은 대상에서 제외된다는 예외를 명시했습니다. timo-api-integration 스킬에는 조회 훅(useHomeViewQuery)과 변경 훅(useWithdrawMutation) 예시 코드를 각각 추가했습니다.



To Reviewers

폴더 이동은 대부분 git mv 기반이라 파일 내용 diff는 거의 없고, import 경로 갱신이 diff의 대부분을 차지합니다. git log상 리네임으로 잡힌 파일들은 내용을 다시 볼 필요 없이 새 경로만 확인해 주시면 됩니다.

hooks/timer/use-active-timer.ts 등 합성 훅을 이번 네이밍 컨벤션 대상에서 제외한 판단이 맞는지 한번 봐주세요 — react-query 결과에 로컬 상태(타이머 tick)를 얹은 훅이라 ~Query 접미사를 붙이면 오히려 오해의 소지가 있다고 판단했습니다.

statistics/_queries/use-statistics-query.ts는 조회 훅 3개를 한 파일에 유지했습니다. 파일당 훅 1개 원칙과는 다르지만, 이미 응집도 높게 묶여 있던 단위라 분리보다 유지가 낫다고 판단했습니다 — 이견 있으시면 알려주세요.

Screenshot 📷



Test Checklist ✔

  • pnpm check-types 통과
  • pnpm lint (--max-warnings 0) 통과
  • pnpm build 프로덕션 빌드 성공 (22개 라우트 정상 생성)
  • 실제 브라우저 동작 확인 — 미실행: 순수 리네임/경로 정리로 런타임 로직 변경 없음

kimminna added 2 commits July 15, 2026 15:16
- api/common, types/, hooks/ 루트에 흩어져 있던 스키마·타입·훅을 도메인별 폴더로 이동했습니다
- 폴더 이동으로 깨진 import 경로를 전체 수정했습니다
- 쿼리/뮤테이션 훅 11개의 파일명·함수명을 -query/-mutation, ~Query/~Mutation 컨벤션으로 통일했습니다
- naming.md에 TanStack Query 훅의 파일명·함수명 컨벤션을 추가했습니다
- timo-api-integration 스킬의 _queries 훅 작성 가이드와 예시를 새 컨벤션에 맞게 갱신했습니다
@github-actions
github-actions Bot requested review from ehye1 and jjangminii July 15, 2026 06:19
@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timo Ready Ready Preview, Comment Jul 15, 2026 6:19am

Request Review

@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ♻ Refactor 기능 개선 및 리팩토링 작업 ♦️ 민아 민아상 labels Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1a23d551-18b0-471b-9eb4-339e7e8a03c1

📥 Commits

Reviewing files that changed from the base of the PR and between fef3dcb and faa3f1d.

📒 Files selected for processing (58)
  • .agents/skills/api/timo-api-integration/SKILL.md
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/use-home-view-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/use-today-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts
  • apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts
  • apps/timo-web/app/[locale]/(main)/focus/_queries/use-focus-todo-query.ts
  • apps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/account/use-logout-mutation.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/withdrawal/use-withdraw-mutation.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsCalendarContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_queries/use-statistics-query.ts
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/app/[locale]/policy/_containers/PolicyContainer.tsx
  • apps/timo-web/app/[locale]/policy/page.tsx
  • apps/timo-web/components/layout/sidebar/time/TimeboxPanel.tsx
  • apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
  • apps/timo-web/containers/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/timer/use-active-timer.ts
  • apps/timo-web/hooks/timer/use-timer-actions.ts
  • apps/timo-web/hooks/timer/use-timer-overtime.ts
  • apps/timo-web/hooks/timer/use-timer-query-invalidation.ts
  • apps/timo-web/hooks/todo-modal/common/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/create/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-time-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-title-field.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-subtask-field.ts
  • apps/timo-web/providers/locale/LanguageSyncProvider.tsx
  • apps/timo-web/providers/overlay/OverlayProvider.tsx
  • apps/timo-web/queries/auth/use-my-profile-query.ts
  • apps/timo-web/queries/settings/use-terms-query.ts
  • apps/timo-web/queries/tag/use-create-tag-mutation.ts
  • apps/timo-web/queries/tag/use-delete-tag-mutation.ts
  • apps/timo-web/queries/tag/use-tags-query.ts
  • apps/timo-web/queries/time-box/use-time-boxes-query.ts
  • apps/timo-web/schemas/auth/user-profile-schema.ts
  • apps/timo-web/schemas/settings/terms-schema.ts
  • apps/timo-web/schemas/tag/tag-schema.ts
  • apps/timo-web/schemas/timebox/timebox-schema.ts
  • apps/timo-web/schemas/timer/timer-schema.ts
  • apps/timo-web/schemas/todo/todo-schema.ts
  • docs/conventions/naming.md

Walkthrough

도메인별 Zod 스키마와 타입을 추가하고, TanStack Query 훅의 명명·경로를 정리했습니다. 관련 화면과 타이머 연동 코드를 새 엔트리포인트로 전환했으며, 오늘 Todo 카드와 OverlayProvider를 추가했습니다.

Changes

스키마 및 Query 훅 구조 정리

Layer / File(s) Summary
도메인 스키마와 타입 계약
apps/timo-web/schemas/*, apps/timo-web/app/.../_types/*
Todo, 타이머, 타임박스, 태그, 약관, 프로필용 Zod 스키마와 추론 타입을 추가했습니다.
Query·Mutation 엔트리포인트 통일
apps/timo-web/queries/*, apps/timo-web/app/.../_queries/*, apps/timo-web/app/.../statistics/*
훅 export 이름을 Query·Mutation 접미사로 변경하고 통계 조회 래퍼 훅을 추가했습니다.
화면 소비처와 import 경로 전환
apps/timo-web/app/..., apps/timo-web/components/*, apps/timo-web/hooks/todo-modal/*
홈, 오늘, 포커스, 설정, 정책, Todo 모달이 새 훅과 도메인 스키마 경로를 사용하도록 변경했습니다.
타이머·Overlay 연동
apps/timo-web/hooks/timer/*, apps/timo-web/providers/*, apps/timo-web/app/[locale]/layout.tsx
타이머 훅 경로를 정리하고 타이머·홈·타임박스 쿼리 무효화 훅과 OverlayProvider 래퍼를 추가했습니다.
오늘 Todo 카드 UI
apps/timo-web/app/.../today/_components/*
Todo 카드의 상태별 표시, 체크·재생 이벤트, 서브투두, 툴바, 키보드 상호작용을 구현했습니다.
규칙 문서화
docs/conventions/naming.md, .agents/skills/api/timo-api-integration/SKILL.md
Query·Mutation 파일명과 함수명 규칙, 훅 작성 및 자가 검토 항목을 문서화했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: 📝 Docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 주요 변경점인 폴더 재정리와 쿼리/뮤테이션 훅 네이밍 통일을 정확히 요약합니다.
Description check ✅ Passed 설명이 변경 범위와 일치하며 스키마·훅 이동과 네이밍 통일을 잘 담고 있습니다.
Linked Issues check ✅ Passed 이슈 #217의 폴더 재정리, import 수정, 11개 훅 네이밍 통일, 문서화 요구를 모두 충족합니다.
Out of Scope Changes check ✅ Passed 요약된 변경은 모두 구조 정리, 경로 수정, 네이밍 규칙 반영에 해당해 벗어난 작업이 보이지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/web/217-query-naming-convention

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 209.87 kB 🔴 415.73 kB
/[locale]/today 193.98 kB 🔴 399.83 kB
/[locale]/focus 160.05 kB 🔴 365.90 kB
/[locale]/settings 166.59 kB 🔴 372.44 kB
/[locale]/statistics 232.91 kB 🔴 438.77 kB
/[locale]/[...rest] 0 B 🟡 205.86 kB
/[locale]/login 212.93 kB 🔴 418.78 kB
/[locale]/oauth/callback 119.64 kB 🟡 325.49 kB
/[locale]/onboarding 232.89 kB 🔴 438.74 kB
/[locale] 118.95 kB 🟡 324.81 kB
/[locale]/policy 125.08 kB 🟡 330.93 kB

공유 번들: 205.86 kB
🟢 < 200kB  |  🟡 < 350kB  |  🔴 ≥ 350kB (First Load JS · gzip)

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 63 🟢 95 🔴 15.8s 🟢 0.000 🟡 471ms
/en/today 🔴 61 🟢 95 🔴 15.5s 🟢 0.000 🟡 530ms
/en/focus 🔴 59 🟢 95 🔴 15.3s 🟢 0.000 🔴 609ms
/en/statistics 🔴 57 🟢 95 🔴 15.2s 🟢 0.000 🔴 698ms

Perf ≥ 70 / A11y ≥ 85 목표
LCP 🟢 < 2.5s 🟡 < 4s 🔴 ≥ 4s  |  CLS 🟢 < 0.1 🟡 < 0.25 🔴 ≥ 0.25  |  TBT 🟢 < 200ms 🟡 < 600ms 🔴 ≥ 600ms

Image Optimization — timo-web
파일 크기 포맷 상태
favicon.png 27.84 kB PNG ⚠️ 🟢
images/google-calendar.png 36.20 kB PNG ⚠️ 🟢
images/google-logo.png 26.79 kB PNG ⚠️ 🟢

총 3개 · 90.84 kB  |  🟢 < 200KB  |  🟡 < 500KB  |  🔴 ≥ 500KB
⚠️ 3개 파일 WebP/AVIF 변환 권장

측정 커밋: a8326d2

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts (1)

29-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

useCreateTodoSubmit 훅의 로직 중복 및 검증 일관성 문제

깔끔하게 생성 로직을 잘 작성해 주셨네요! ✨

살펴보니 두 파일에 동일한 역할을 수행하는 비즈니스 훅이 나뉘어 존재하고 있습니다. 특히 전역 훅에는 응답을 검증하는 safeParse 로직이 있지만, 오늘(Today) 화면용 훅에는 해당 로직이 누락되어 있어 동작에 일관성이 부족한 상황입니다.
로직 파편화를 방지하고 유지보수성을 높이기 위해, 하나의 훅으로 깔끔하게 통합하고 외부에서 무효화(invalidate)할 쿼리 키를 주입받는 구조로 개선하는 것을 추천합니다. React 공식 문서 - Hook 추출하기

  • apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts#L29-L60: 메인 훅으로서 매개변수를 통해 queryKeysToInvalidate를 주입받을 수 있도록 수정해 보세요.
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts#L28-L52: 해당 중복 훅을 제거하고, 위에서 수정된 전역 훅에 필요한 쿼리 키를 넘겨 재사용하도록 교체하세요.
💡 리팩터링 아이디어 (참고용)
import type { QueryKey } from "`@tanstack/react-query`";

export const useCreateTodoSubmit = (queryKeysToInvalidate: QueryKey[]) => {
  // ... (동일한 초기화 및 빌드 로직)
  
  const handleSubmit = (data: CreateTodoRequest) => {
    createTodo(
      { data: buildCreateTodoRequestBody(data) },
      {
        onSuccess: (response) => {
          const parsed = todoCreateResponseSchema.safeParse(response.data);

          if (!parsed.success) {
            setIsErrorToastOpen(true);
            return;
          }

          queryKeysToInvalidate.forEach((key) =>
            queryClient.invalidateQueries({ queryKey: key })
          );
        },
        onError: () => {
          setIsErrorToastOpen(true);
        },
      }
    );
  };
  
  // ...
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts` around lines
29 - 60, The duplicated create-submit hooks lack a shared, consistent
response-validation flow. In
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts:29-60, update
useCreateTodoSubmit to accept queryKeysToInvalidate: QueryKey[] and invalidate
each supplied key after successful todoCreateResponseSchema validation; preserve
the existing error-toast behavior. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:28-52,
remove the duplicate hook implementation and reuse the global
useCreateTodoSubmit, passing the Today screen’s required query keys.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts (1)

120-130: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

에러 발생 시 낙관적 업데이트를 롤백하는 로직이 누락되었습니다.

투두 완료 상태를 변경하는 changeTodoStatus API 호출이 실패할 경우, UI에만 완료 처리된 상태로 남아있게 됩니다. 데이터 일관성을 위해 onError 시 이전 상태로 되돌리는 롤백 처리를 추가해 주세요. 😉

🛠️ 롤백 처리를 포함한 수정 제안
           updateTodo(todoId, (todo) => ({ ...todo, completed: true }));
           changeTodoStatus(
             { todoId, data: { isCompleted: true, date: dateKey } },
             {
               onSuccess: () => {
                 invalidateTodayView();
                 invalidateTodoDetail(todoId, dateKey);
               },
+              onError: () => {
+                updateTodo(todoId, (todo) => ({ ...todo, completed: false }));
+              },
             },
           );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
around lines 120 - 130, In the todo completion flow around updateTodo and
changeTodoStatus, preserve the todo’s previous state before the optimistic
completed update, then add an onError handler to restore that state when the API
call fails. Keep the existing onSuccess invalidation behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx:
- Around line 85-99: Update TodayTodoCard’s handleCardKeyDown and card
interaction structure so the clickable area is a separate native button rather
than a container with role="button", keeping checkbox, playback, and toolbar
controls outside that button. Render the toolbar as non-interactive presentation
where appropriate, and ensure keyboard activation only occurs when e.target ===
e.currentTarget so Enter/Space from nested controls cannot trigger onCardClick.
- Around line 181-195: Update TodayTodoCardToolbar and its call site in
TodayTodoCard so all user-facing labels currently hardcoded in Korean—tagLabel,
detailHeading, and repeat option labels—are supplied through a locale-aware
props contract from the parent container. Keep _components/ locale-agnostic by
removing direct translation or locale handling there, and ensure each [locale]
renders the injected translations.

In `@apps/timo-web/schemas/auth/user-profile-schema.ts`:
- Around line 3-12: Strengthen userProfileSchema validation by requiring id to
be an integer with Zod’s integer constraint and validating email with Zod’s
email-format validator. Leave the remaining profile fields and their nullish
behavior unchanged.

In `@apps/timo-web/schemas/settings/terms-schema.ts`:
- Around line 13-14: Rename TermsType to TermsTypes and convert TermsDetail to
an interface in terms-schema.ts; update all related imports and parameter or
prop annotations in use-terms-query.ts and SettingsTermsContainer.tsx, including
SettingsTermsContainerProps, while keeping Props declarations as interfaces and
reserving type aliases for unions, tuples, and literals.

In `@apps/timo-web/schemas/timebox/timebox-schema.ts`:
- Line 30: Replace the inferred object type aliases with interface declarations:
update TimeBox in apps/timo-web/schemas/timebox/timebox-schema.ts at lines 30-30
and ActiveTimer in apps/timo-web/schemas/timer/timer-schema.ts at lines 18-18 to
extend their respective Zod inference types, preserving the existing
schema-derived shapes.

In `@apps/timo-web/schemas/todo/todo-schema.ts`:
- Around line 20-28: Move the existing dayOfWeekSchema declaration above
todoRepeatWeekdaySchema and reuse it for the repeat-weekday schema instead of
defining the identical weekday enum twice. Remove the later duplicate
dayOfWeekSchema declaration while preserving the current weekday values and
exported schema behavior.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts:
- Around line 120-130: In the todo completion flow around updateTodo and
changeTodoStatus, preserve the todo’s previous state before the optimistic
completed update, then add an onError handler to restore that state when the API
call fails. Keep the existing onSuccess invalidation behavior unchanged.

In `@apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts`:
- Around line 29-60: The duplicated create-submit hooks lack a shared,
consistent response-validation flow. In
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts:29-60, update
useCreateTodoSubmit to accept queryKeysToInvalidate: QueryKey[] and invalidate
each supplied key after successful todoCreateResponseSchema validation; preserve
the existing error-toast behavior. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:28-52,
remove the duplicate hook implementation and reuse the global
useCreateTodoSubmit, passing the Today screen’s required query keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1a23d551-18b0-471b-9eb4-339e7e8a03c1

📥 Commits

Reviewing files that changed from the base of the PR and between fef3dcb and faa3f1d.

📒 Files selected for processing (58)
  • .agents/skills/api/timo-api-integration/SKILL.md
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/use-home-view-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/use-today-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts
  • apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts
  • apps/timo-web/app/[locale]/(main)/focus/_queries/use-focus-todo-query.ts
  • apps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/account/use-logout-mutation.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/withdrawal/use-withdraw-mutation.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsCalendarContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_queries/use-statistics-query.ts
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/app/[locale]/policy/_containers/PolicyContainer.tsx
  • apps/timo-web/app/[locale]/policy/page.tsx
  • apps/timo-web/components/layout/sidebar/time/TimeboxPanel.tsx
  • apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
  • apps/timo-web/containers/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/timer/use-active-timer.ts
  • apps/timo-web/hooks/timer/use-timer-actions.ts
  • apps/timo-web/hooks/timer/use-timer-overtime.ts
  • apps/timo-web/hooks/timer/use-timer-query-invalidation.ts
  • apps/timo-web/hooks/todo-modal/common/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/create/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-time-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-title-field.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-subtask-field.ts
  • apps/timo-web/providers/locale/LanguageSyncProvider.tsx
  • apps/timo-web/providers/overlay/OverlayProvider.tsx
  • apps/timo-web/queries/auth/use-my-profile-query.ts
  • apps/timo-web/queries/settings/use-terms-query.ts
  • apps/timo-web/queries/tag/use-create-tag-mutation.ts
  • apps/timo-web/queries/tag/use-delete-tag-mutation.ts
  • apps/timo-web/queries/tag/use-tags-query.ts
  • apps/timo-web/queries/time-box/use-time-boxes-query.ts
  • apps/timo-web/schemas/auth/user-profile-schema.ts
  • apps/timo-web/schemas/settings/terms-schema.ts
  • apps/timo-web/schemas/tag/tag-schema.ts
  • apps/timo-web/schemas/timebox/timebox-schema.ts
  • apps/timo-web/schemas/timer/timer-schema.ts
  • apps/timo-web/schemas/todo/todo-schema.ts
  • docs/conventions/naming.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts (1)

29-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

useCreateTodoSubmit 훅의 로직 중복 및 검증 일관성 문제

깔끔하게 생성 로직을 잘 작성해 주셨네요! ✨

살펴보니 두 파일에 동일한 역할을 수행하는 비즈니스 훅이 나뉘어 존재하고 있습니다. 특히 전역 훅에는 응답을 검증하는 safeParse 로직이 있지만, 오늘(Today) 화면용 훅에는 해당 로직이 누락되어 있어 동작에 일관성이 부족한 상황입니다.
로직 파편화를 방지하고 유지보수성을 높이기 위해, 하나의 훅으로 깔끔하게 통합하고 외부에서 무효화(invalidate)할 쿼리 키를 주입받는 구조로 개선하는 것을 추천합니다. React 공식 문서 - Hook 추출하기

  • apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts#L29-L60: 메인 훅으로서 매개변수를 통해 queryKeysToInvalidate를 주입받을 수 있도록 수정해 보세요.
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts#L28-L52: 해당 중복 훅을 제거하고, 위에서 수정된 전역 훅에 필요한 쿼리 키를 넘겨 재사용하도록 교체하세요.
💡 리팩터링 아이디어 (참고용)
import type { QueryKey } from "`@tanstack/react-query`";

export const useCreateTodoSubmit = (queryKeysToInvalidate: QueryKey[]) => {
  // ... (동일한 초기화 및 빌드 로직)
  
  const handleSubmit = (data: CreateTodoRequest) => {
    createTodo(
      { data: buildCreateTodoRequestBody(data) },
      {
        onSuccess: (response) => {
          const parsed = todoCreateResponseSchema.safeParse(response.data);

          if (!parsed.success) {
            setIsErrorToastOpen(true);
            return;
          }

          queryKeysToInvalidate.forEach((key) =>
            queryClient.invalidateQueries({ queryKey: key })
          );
        },
        onError: () => {
          setIsErrorToastOpen(true);
        },
      }
    );
  };
  
  // ...
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts` around lines
29 - 60, The duplicated create-submit hooks lack a shared, consistent
response-validation flow. In
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts:29-60, update
useCreateTodoSubmit to accept queryKeysToInvalidate: QueryKey[] and invalidate
each supplied key after successful todoCreateResponseSchema validation; preserve
the existing error-toast behavior. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:28-52,
remove the duplicate hook implementation and reuse the global
useCreateTodoSubmit, passing the Today screen’s required query keys.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts (1)

120-130: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

에러 발생 시 낙관적 업데이트를 롤백하는 로직이 누락되었습니다.

투두 완료 상태를 변경하는 changeTodoStatus API 호출이 실패할 경우, UI에만 완료 처리된 상태로 남아있게 됩니다. 데이터 일관성을 위해 onError 시 이전 상태로 되돌리는 롤백 처리를 추가해 주세요. 😉

🛠️ 롤백 처리를 포함한 수정 제안
           updateTodo(todoId, (todo) => ({ ...todo, completed: true }));
           changeTodoStatus(
             { todoId, data: { isCompleted: true, date: dateKey } },
             {
               onSuccess: () => {
                 invalidateTodayView();
                 invalidateTodoDetail(todoId, dateKey);
               },
+              onError: () => {
+                updateTodo(todoId, (todo) => ({ ...todo, completed: false }));
+              },
             },
           );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
around lines 120 - 130, In the todo completion flow around updateTodo and
changeTodoStatus, preserve the todo’s previous state before the optimistic
completed update, then add an onError handler to restore that state when the API
call fails. Keep the existing onSuccess invalidation behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx:
- Around line 85-99: Update TodayTodoCard’s handleCardKeyDown and card
interaction structure so the clickable area is a separate native button rather
than a container with role="button", keeping checkbox, playback, and toolbar
controls outside that button. Render the toolbar as non-interactive presentation
where appropriate, and ensure keyboard activation only occurs when e.target ===
e.currentTarget so Enter/Space from nested controls cannot trigger onCardClick.
- Around line 181-195: Update TodayTodoCardToolbar and its call site in
TodayTodoCard so all user-facing labels currently hardcoded in Korean—tagLabel,
detailHeading, and repeat option labels—are supplied through a locale-aware
props contract from the parent container. Keep _components/ locale-agnostic by
removing direct translation or locale handling there, and ensure each [locale]
renders the injected translations.

In `@apps/timo-web/schemas/auth/user-profile-schema.ts`:
- Around line 3-12: Strengthen userProfileSchema validation by requiring id to
be an integer with Zod’s integer constraint and validating email with Zod’s
email-format validator. Leave the remaining profile fields and their nullish
behavior unchanged.

In `@apps/timo-web/schemas/settings/terms-schema.ts`:
- Around line 13-14: Rename TermsType to TermsTypes and convert TermsDetail to
an interface in terms-schema.ts; update all related imports and parameter or
prop annotations in use-terms-query.ts and SettingsTermsContainer.tsx, including
SettingsTermsContainerProps, while keeping Props declarations as interfaces and
reserving type aliases for unions, tuples, and literals.

In `@apps/timo-web/schemas/timebox/timebox-schema.ts`:
- Line 30: Replace the inferred object type aliases with interface declarations:
update TimeBox in apps/timo-web/schemas/timebox/timebox-schema.ts at lines 30-30
and ActiveTimer in apps/timo-web/schemas/timer/timer-schema.ts at lines 18-18 to
extend their respective Zod inference types, preserving the existing
schema-derived shapes.

In `@apps/timo-web/schemas/todo/todo-schema.ts`:
- Around line 20-28: Move the existing dayOfWeekSchema declaration above
todoRepeatWeekdaySchema and reuse it for the repeat-weekday schema instead of
defining the identical weekday enum twice. Remove the later duplicate
dayOfWeekSchema declaration while preserving the current weekday values and
exported schema behavior.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts:
- Around line 120-130: In the todo completion flow around updateTodo and
changeTodoStatus, preserve the todo’s previous state before the optimistic
completed update, then add an onError handler to restore that state when the API
call fails. Keep the existing onSuccess invalidation behavior unchanged.

In `@apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts`:
- Around line 29-60: The duplicated create-submit hooks lack a shared,
consistent response-validation flow. In
apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts:29-60, update
useCreateTodoSubmit to accept queryKeysToInvalidate: QueryKey[] and invalidate
each supplied key after successful todoCreateResponseSchema validation; preserve
the existing error-toast behavior. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:28-52,
remove the duplicate hook implementation and reuse the global
useCreateTodoSubmit, passing the Today screen’s required query keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1a23d551-18b0-471b-9eb4-339e7e8a03c1

📥 Commits

Reviewing files that changed from the base of the PR and between fef3dcb and faa3f1d.

📒 Files selected for processing (58)
  • .agents/skills/api/timo-api-integration/SKILL.md
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_queries/use-home-view-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/home-view-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_queries/use-today-query.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_types/today-type.ts
  • apps/timo-web/app/[locale]/(main)/focus/_hooks/use-focus-session.ts
  • apps/timo-web/app/[locale]/(main)/focus/_queries/use-focus-todo-query.ts
  • apps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_containers/withdrawal/SettingsWithdrawalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/settings/_hooks/account/use-settings-profile.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/account/use-logout-mutation.ts
  • apps/timo-web/app/[locale]/(main)/settings/_queries/withdrawal/use-withdraw-mutation.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsCalendarContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_queries/use-statistics-query.ts
  • apps/timo-web/app/[locale]/layout.tsx
  • apps/timo-web/app/[locale]/policy/_containers/PolicyContainer.tsx
  • apps/timo-web/app/[locale]/policy/page.tsx
  • apps/timo-web/components/layout/sidebar/time/TimeboxPanel.tsx
  • apps/timo-web/components/layout/sidebar/time/TimerPanel.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
  • apps/timo-web/containers/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/containers/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/hooks/timer/use-active-timer.ts
  • apps/timo-web/hooks/timer/use-timer-actions.ts
  • apps/timo-web/hooks/timer/use-timer-overtime.ts
  • apps/timo-web/hooks/timer/use-timer-query-invalidation.ts
  • apps/timo-web/hooks/todo-modal/common/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/create/use-create-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/create/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-time-field.ts
  • apps/timo-web/hooks/todo-modal/create/use-title-field.ts
  • apps/timo-web/hooks/todo-modal/detail/use-detail-subtask-field.ts
  • apps/timo-web/providers/locale/LanguageSyncProvider.tsx
  • apps/timo-web/providers/overlay/OverlayProvider.tsx
  • apps/timo-web/queries/auth/use-my-profile-query.ts
  • apps/timo-web/queries/settings/use-terms-query.ts
  • apps/timo-web/queries/tag/use-create-tag-mutation.ts
  • apps/timo-web/queries/tag/use-delete-tag-mutation.ts
  • apps/timo-web/queries/tag/use-tags-query.ts
  • apps/timo-web/queries/time-box/use-time-boxes-query.ts
  • apps/timo-web/schemas/auth/user-profile-schema.ts
  • apps/timo-web/schemas/settings/terms-schema.ts
  • apps/timo-web/schemas/tag/tag-schema.ts
  • apps/timo-web/schemas/timebox/timebox-schema.ts
  • apps/timo-web/schemas/timer/timer-schema.ts
  • apps/timo-web/schemas/todo/todo-schema.ts
  • docs/conventions/naming.md
🛑 Comments failed to post (6)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx (2)

85-99: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

카드 전체를 button으로 모델링하지 말고 내부 컨트롤과 분리해 주세요.

role="button" 내부에 체크박스·재생 버튼·툴바 컨트롤이 중첩되어 접근성 트리가 불안정합니다. 특히 pointer-events-none은 키보드 포커스를 막지 않으므로, 툴바에서 Enter/Space를 누르면 이벤트가 Line 85의 핸들러로 전파되어 카드 클릭까지 실행됩니다.

카드의 클릭 영역을 별도 네이티브 버튼으로 분리하고, 툴바는 비대화형 표시 컴포넌트로 렌더링하세요. 임시 방어로는 e.target === e.currentTarget일 때만 카드 키보드 동작을 실행해야 합니다.

참고: MDN ARIA button role, MDN pointer-events

Also applies to: 108-157, 172-198

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
around lines 85 - 99, Update TodayTodoCard’s handleCardKeyDown and card
interaction structure so the clickable area is a separate native button rather
than a container with role="button", keeping checkbox, playback, and toolbar
controls outside that button. Render the toolbar as non-interactive presentation
where appropriate, and ensure keyboard activation only occurs when e.target ===
e.currentTarget so Enter/Space from nested controls cannot trigger onCardClick.

181-195: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

로케일별 문구를 props로 전달해 주세요.

태그, 상세 설정, 반복 옵션 등의 한국어 문자열이 고정되어 다른 [locale]에서도 한국어로 노출됩니다. 번역된 라벨을 컨테이너에서 주입하도록 TodayTodoCardToolbar 계약에 추가해 주세요.

참고: Next.js Internationalization

As per path instructions, "_components/: props만 받는 순수 UI" 규칙에 따라 로케일 처리는 상위 계층에 두어야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_components/TodayTodoCard.tsx
around lines 181 - 195, Update TodayTodoCardToolbar and its call site in
TodayTodoCard so all user-facing labels currently hardcoded in Korean—tagLabel,
detailHeading, and repeat option labels—are supplied through a locale-aware
props contract from the parent container. Keep _components/ locale-agnostic by
removing direct translation or locale handling there, and ensure each [locale]
renders the injected translations.

Source: Path instructions

apps/timo-web/schemas/auth/user-profile-schema.ts (1)

3-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

스키마 검증 강도를 높여보는 것은 어떨까요?

Zod의 향상된 기능을 활용하여 id와 이메일 필드를 더 안전하게 정의할 수 있습니다. id에는 .int()를 추가하여 소수점이 포함되지 않은 정수만 허용하도록 하고, 이메일 필드에는 이메일 형식을 엄격하게 검증하도록 수정하는 것을 제안합니다. 이렇게 하면 데이터 무결성을 높이고 예기치 않은 데이터 오류를 사전에 방지할 수 있습니다.

참고: Zod 공식 문서 - Strings

💡 향상된 스키마 제안
 export const userProfileSchema = z.object({
-  id: z.number(),
+  id: z.number().int(),
   name: z.string(),
-  email: z.string(),
+  email: z.string().email(),
   profileImageUrl: z.string().nullish(),
   language: z.string(),
   zoneId: z.string(),
   calendarConnected: z.boolean(),
-  calendarEmail: z.string().nullish(),
+  calendarEmail: z.string().email().nullish(),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export const userProfileSchema = z.object({
  id: z.number().int(),
  name: z.string(),
  email: z.string().email(),
  profileImageUrl: z.string().nullish(),
  language: z.string(),
  zoneId: z.string(),
  calendarConnected: z.boolean(),
  calendarEmail: z.string().email().nullish(),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/schemas/auth/user-profile-schema.ts` around lines 3 - 12,
Strengthen userProfileSchema validation by requiring id to be an integer with
Zod’s integer constraint and validating email with Zod’s email-format validator.
Leave the remaining profile fields and their nullish behavior unchanged.
apps/timo-web/schemas/settings/terms-schema.ts (1)

13-14: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

타입 네이밍 가이드라인 준수를 위한 일괄 수정 제안

타입 안전성 가이드라인에 따라 객체 타입은 interface로 선언하고, type alias에는 Types 접미사를 사용해야 합니다. 이에 맞춰 타입 선언과 관련된 참조를 모두 업데이트해 주세요. 구조화된 네이밍은 유지보수에 큰 도움이 됩니다! 😊

  • apps/timo-web/schemas/settings/terms-schema.ts#L13-L14: TermsDetailinterface로 선언하고, TermsTypeTermsTypes로 수정합니다.
  • apps/timo-web/queries/settings/use-terms-query.ts#L10-L20: import 구문과 매개변수 타입을 TermsTypes로 변경합니다.
  • apps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsx#L5-L18: import 구문을 TermsTypes로 변경하고, 연관된 SettingsTermsContainerProps의 타입(11번 줄)도 함께 업데이트해야 합니다.

As per path instructions: Props 타입은 interface로 선언, type alias는 유니언·튜플·리터럴에만 사용, 접미사 Types

💡 Proposed fixes for the schema file
-export type TermsType = z.infer<typeof termsTypeSchema>;
-export type TermsDetail = z.infer<typeof termsDetailSchema>;
+export type TermsTypes = z.infer<typeof termsTypeSchema>;
+
+export interface TermsDetail extends z.infer<typeof termsDetailSchema> {}
📍 Affects 3 files
  • apps/timo-web/schemas/settings/terms-schema.ts#L13-L14 (this comment)
  • apps/timo-web/queries/settings/use-terms-query.ts#L10-L20
  • apps/timo-web/app/[locale]/(main)/settings/_containers/terms/SettingsTermsContainer.tsx#L5-L18
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/schemas/settings/terms-schema.ts` around lines 13 - 14, Rename
TermsType to TermsTypes and convert TermsDetail to an interface in
terms-schema.ts; update all related imports and parameter or prop annotations in
use-terms-query.ts and SettingsTermsContainer.tsx, including
SettingsTermsContainerProps, while keeping Props declarations as interfaces and
reserving type aliases for unions, tuples, and literals.

Source: Path instructions

apps/timo-web/schemas/timebox/timebox-schema.ts (1)

30-30: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

객체 타입 선언 시 interface를 사용해 주세요.

경로 규칙(Path Instructions)에 따르면 type 별칭은 유니언·튜플·리터럴에만 사용해야 하며, 일반 객체 타입은 interface를 사용해야 합니다. Zod 객체 스키마의 추론 타입에도 interface 확장을 사용하여 코드베이스의 일관성을 높여주세요! 🚀

  • apps/timo-web/schemas/timebox/timebox-schema.ts#L30-L30: export interface TimeBox extends z.infer<typeof timeBoxSchema> {}로 변경하세요.
  • apps/timo-web/schemas/timer/timer-schema.ts#L18-L18: export interface ActiveTimer extends z.infer<typeof activeTimerSchema> {}로 변경하세요.
📍 Affects 2 files
  • apps/timo-web/schemas/timebox/timebox-schema.ts#L30-L30 (this comment)
  • apps/timo-web/schemas/timer/timer-schema.ts#L18-L18
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/schemas/timebox/timebox-schema.ts` at line 30, Replace the
inferred object type aliases with interface declarations: update TimeBox in
apps/timo-web/schemas/timebox/timebox-schema.ts at lines 30-30 and ActiveTimer
in apps/timo-web/schemas/timer/timer-schema.ts at lines 18-18 to extend their
respective Zod inference types, preserving the existing schema-derived shapes.

Source: Path instructions

apps/timo-web/schemas/todo/todo-schema.ts (1)

20-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

dayOfWeekSchema 재사용을 통한 중복 제거

훌륭하게 스키마를 구성해 주셨네요! 👏

하나 제안을 드리자면, 현재 todoRepeatWeekdaySchema와 74번째 라인의 dayOfWeekSchema가 완전히 동일한 요일 리스트를 가지고 있습니다. Zod의 중복 선언을 줄이기 위해 dayOfWeekSchema를 위로 올려서 선언하고, 이를 재사용하는 건 어떨까요?
Zod 공식 문서에서도 DRY 원칙을 권장하고 있습니다. 유지보수하기 한결 편해질 거예요!

💡 개선 제안
-export const todoRepeatWeekdaySchema = z.enum([
+export const dayOfWeekSchema = z.enum([
   "MON",
   "TUE",
   "WED",
   "THU",
   "FRI",
   "SAT",
   "SUN",
 ]);
+
+export const todoRepeatWeekdaySchema = dayOfWeekSchema;

(수정 후, 기존 74~82 라인에 있던 dayOfWeekSchema 선언은 삭제해 주시면 완벽합니다!)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/timo-web/schemas/todo/todo-schema.ts` around lines 20 - 28, Move the
existing dayOfWeekSchema declaration above todoRepeatWeekdaySchema and reuse it
for the repeat-weekday schema instead of defining the identical weekday enum
twice. Remove the later duplicate dayOfWeekSchema declaration while preserving
the current weekday values and exported schema behavior.

@yumin-kim2 yumin-kim2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굿굿입니다 파일이 점점 많아질수록 역할이 헷갈릴 수 있는데, 이렇게 역할별로 분리해두니 관리하기 훨씬 편할 것 같네요 짱짱맨

@kimminna
kimminna merged commit f589d7f into develop Jul 15, 2026
15 checks passed
@kimminna
kimminna deleted the refactor/web/217-query-naming-convention branch July 15, 2026 06:41
@kimminna kimminna mentioned this pull request Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⏰ Timo-web Timo 웹 서비스 ♦️ 민아 민아상 ♻ Refactor 기능 개선 및 리팩토링 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 폴더 구조 정리 및 쿼리/뮤테이션 훅 네이밍 컨벤션 적용

2 participants